package com.sk.orm.support;

import com.sk.util.LambdaUtil;
import lombok.Getter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @author smy
 * {@code @date} 2023/12/18
 */
@Getter
public class CriteriaSupport<V> {
    private final Function<Object, V> vf;
    private String select = "";
    private String from = "";
    private String where = "";
    private String groupBy = "";
    private String having = "";
    private String orderBy = "";
    private Integer offset;
    private Integer size;
    private final Map<String, Object> paramMap = new HashMap<>();
    private int pNo = 0;

    public CriteriaSupport(Criteria<V> criteria) {
        vf = criteria.vf;
        select += " select " + criteria.select;
        from += " from " + criteria.from.getSimpleName() + " as " + criteria.fromAs;

        dealJoin(criteria);
        dealWhere(criteria);
        dealGroupBy(criteria);
        dealHaving(criteria);
        dealOrderBy(criteria);
        offset = criteria.offset;
        size = criteria.size;
    }

    private void dealGroupBy(Criteria<V> criteria) {
        if (StringUtils.hasText(criteria.groupBy)) {
            groupBy = " group by " + criteria.groupBy;
        }
    }


    private void dealJoin(Criteria<V> criteria) {
        criteria.join.forEach(j -> {
            from += " " + j;
        });
    }

    private void dealWhere(Criteria<V> criteria) {
        criteria.where.forEach(LambdaUtil.enumerate((w, i) -> {
            if (i == 0) {
                where += " where ";
            } else {
                where += " and ";
            }
            where += dealWhere(criteria.fromAs, w);
        }));
    }

    private void dealHaving(Criteria<V> criteria) {
        criteria.having.forEach(LambdaUtil.enumerate((w, i) -> {
            if (i == 0) {
                having += " having ";
            } else {
                having += " and ";
            }
            having += dealWhere(criteria.fromAs, w);
        }));
    }

    private String field2Name(String fromAs, String fieldName) {
        if (fieldName.contains(".") || fieldName.contains("(")) {
            return fieldName;
        }
        return fromAs + "." + fieldName;
    }

    @SuppressWarnings("unchecked")
    private String dealWhere(String fromAs, WhereData whereData) {
        StringBuilder s = new StringBuilder();
        switch (whereData.getType()) {
            case and:
                Assert.isTrue(!whereData.isNot(), "and不支持not设置");
                Collection<WhereData> collection1 = (Collection<WhereData>) whereData.getValue();
                s.append("(");
                collection1.forEach(LambdaUtil.enumerate((sub, i) -> {
                    if (i != 0) {
                        s.append(" and ");
                    }
                    s.append(dealWhere(fromAs, sub));
                }));
                s.append(")");
                break;
            case or:
                Assert.isTrue(!whereData.isNot(), "not不支持not设置");
                Collection<WhereData> collection2 = (Collection<WhereData>) whereData.getValue();
                s.append("(");
                collection2.forEach(LambdaUtil.enumerate((sub, i) -> {
                    if (i != 0) {
                        s.append(" or ");
                    }
                    s.append(dealWhere(fromAs, sub));
                }));
                s.append(")");
                break;
            case query:
                Assert.isTrue(!whereData.isNot(), "jsql不支持not设置");
                s.append("(").append(whereData.getKey()).append(")");
                if (whereData.getValue() != null) {
                    Map<String, Object> param = (Map<String, Object>) whereData.getValue();
                    paramMap.putAll(param);
                }
                break;
            default:
                s.append(dealWhereDataEasy(fromAs, whereData));
        }
        pNo++;
        return s.toString();
    }

    private String dealWhereDataEasy(String fromAs, WhereData whereData) {
        StringBuilder s = new StringBuilder();
        Object value = whereData.getValue();
        s.append(field2Name(fromAs, whereData.getKey()));
        String symbol;
        switch (whereData.getType()) {
            case eq:
                symbol = whereData.isNot() ? " != " : " = ";
                s.append(symbol).append(":p").append(pNo);
                paramMap.put("p" + pNo, value);
                break;
            case like:
                symbol = whereData.isNot() ? " not like " : " like ";
                s.append(symbol).append(":p").append(pNo);
                paramMap.put("p" + pNo, "%" + value + "%");
                break;
            case leftLike:
                symbol = whereData.isNot() ? " not like " : " like ";
                s.append(symbol).append(":p").append(pNo);
                paramMap.put("p" + pNo, value + "%");
                break;
            case rightLike:
                symbol = whereData.isNot() ? " not like " : " like ";
                s.append(symbol).append(":p").append(pNo);
                paramMap.put("p" + pNo, "%" + value);
                break;
            case ge:
                Assert.isTrue(!whereData.isNot(), "比较函数不支持not设置");
                s.append(" >= ").append(":p").append(pNo);
                paramMap.put("p" + pNo, value);
                break;
            case gt:
                Assert.isTrue(!whereData.isNot(), "比较函数不支持not设置");
                s.append(" > ").append(":p").append(pNo);
                paramMap.put("p" + pNo, value);
                break;
            case le:
                Assert.isTrue(!whereData.isNot(), "比较函数不支持not设置");
                s.append(" <= ").append(":p").append(pNo);
                paramMap.put("p" + pNo, value);
                break;
            case lt:
                Assert.isTrue(!whereData.isNot(), "比较函数不支持not设置");
                s.append(" < ").append(":p").append(pNo);
                paramMap.put("p" + pNo, value);
                break;
            case in:
                symbol = whereData.isNot() ? " not in " : " in ";
                s.append(symbol).append("(:p").append(pNo).append(")");
                paramMap.put("p" + pNo, value);
                break;
            case isNull:
                if (!whereData.isNot()) {
                    s.append(" is null ");
                } else {
                    s.append(" is not null ");
                }
                break;
            default:
                throw new RuntimeException("sql 不支持" + whereData.getType());
        }
        return s.toString();
    }


    private void dealOrderBy(Criteria<V> criteria) {
        criteria.orderBy.forEach(LambdaUtil.enumerate((s, i) -> {
            if (i == 0) {
                orderBy += " order by ";
            } else {
                orderBy += ",";
            }
            String sortField = s.getSortField();
            if (!sortField.contains(".")) {
                sortField = criteria.fromAs + "." + s.getSortField();
            }
            orderBy += sortField;
            if (StringUtils.hasText(s.getSortOrder())) {
                orderBy += " " + s.getSortOrder();
            }
        }));
    }


}
